package cz.worktracker.controller;

import cz.worktracker.enums.DataSetsEnum;
import cz.worktracker.model.entities.Customer;
import cz.worktracker.model.entities.Project;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.Map;
import java.util.logging.Level;
import javax.annotation.ManagedBean;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.enterprise.context.SessionScoped;
import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import javax.faces.model.DataModel;
import javax.faces.model.ListDataModel;
import javax.inject.Inject;
import javax.inject.Named;
import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.Transaction;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;


/**
 * ProjectManager
 * Vytvoření nového projektu
 * @since 2010-04-23
 * @author kyjovsky
 */

@ManagedBean
@SessionScoped
@Named
public class ProjectManager  extends AbstractManager
                                implements Serializable {

    private static final long serialVersionUID = 1L;

    private static final String NAV_LIST = "/user/project/list";
    private static final String NAV_EDIT = "/user/project/edit";
    private static final String NAV_DETAIL = "/user/project/detail";
    private static final String NAV_REDIRECT = "?faces-redirect=true";

    private Project project;
    private AbstractPaginator paginator;
    private DataModel dataModel;
    private DataSetsEnum DSEnum;
    private int rowsPerPage = DSEnum.ITEMS_PER_PAGE_DEFAULT.getValue();
    
    private String searchPattern = "";
    
    private String sortByField = DSEnum.SORT_BY_FIELD.getStringValue();
    private String sortDirection = DSEnum.SORT_DIRECTION.getStringValue();

    private boolean nullFlag = true;

    @Inject UserManager userManager;
    @Inject CustomerManager customerManager;
    @Inject TaskManager taskManager;

    @PostConstruct
    public void construct() {
        init();
    }


    @PreDestroy
    public void destroy() {
	project = null;
        paginator = null;
        dataModel = null;
    }


    private void init() {
        if( project == null ) {
            project = new Project();
            project.setIdUser(-1);
            dataModel = null;
            paginator = null;
        }
    }

    
    public boolean getNewProject() {
        if( isNullFlag() ) {
            project = null;
            init();
        }
        
        setNullFlag( true );

        return true;
    }


    public String create() {

        if (project != null) {
            try {
                project.setIdUser(userManager.getUser().getId());
                project.setIsActive("y");
                project.setCreate(new Date());
                if(project.getPricePerRateUnit() == null) project.setPricePerRateUnit(new Float (0.0));
                Transaction tx = getSession().beginTransaction();
                getSession().save(project);
                tx.commit();
                init();
            } catch (Exception e) {
                getLogger(getClass()).log(Level.SEVERE, "Error on try to save Project: " + getProject(), e);
                addMessage("Error on try to save Project", FacesMessage.SEVERITY_ERROR);
                return null;
            }
        }
        
        return NAV_LIST;
    }

    /**
     * Method sets current user record for editation.
     * @return
     */
    public String edit() {
        project = (Project) getList().getRowData();

        return NAV_EDIT + NAV_REDIRECT;
    }

     public String newTask() {
        
        project = (Project) getList().getRowData();
        resetDataModel();
        taskManager.resetDataModel();
        return "/user/task/create" + NAV_REDIRECT;
    }


     public String detail() {

        project = (Project) getList().getRowData();
        resetDataModel();
        taskManager.resetDataModel();
        return NAV_DETAIL + NAV_REDIRECT;
     }

     public String showList() {
        resetDataModel();
        return "/user/project/list" + NAV_REDIRECT;
     }



    public String update() {
        if (project != null) {
            try {
                if(project.getPricePerRateUnit() == null) project.setPricePerRateUnit(new Float (0.0));
                Transaction tx = getSession().beginTransaction();
                getSession().update(project);
                tx.commit();
                project = null;
                init();
            } catch (Exception e) {
                getLogger(getClass()).log(Level.SEVERE, "Error on try to save Project: " + getProject(), e);
                addMessage("Error on try to save Project", FacesMessage.SEVERITY_ERROR);
                return null;
            }
        }
        return NAV_LIST + NAV_REDIRECT;
    }


    /**
     * Prepares dataModel.
     * @return Hell I really want to know.
     */
    public String prepareDataModel() {
        resetDataModel();
        return NAV_LIST + NAV_REDIRECT;
    }
    

    /**
     * Method nulls reference of dataModel.
     */
    private void resetDataModel() {
        dataModel = null;
    }


    /**
     * Method nulls reference of paginator.
     */
    public void resetPagination() {
        paginator = null;
    }


    /**
     * Returns page switcher and override abstract methods from AbstractPaginator.
     * @return Paginator object.
     */
    public AbstractPaginator getPaginator() {
        if( paginator == null ) {
            paginator = new AbstractPaginator( rowsPerPage ) {

               /*
                * While creating a new instance we have to implement abstract
                * methods defined in AbstractPaginator class.
                */

                @Override
                public int getItemsCount() {
                    return getProjectsCount();
                }

                @Override
                public DataModel createPageDataModel() {
                    ListDataModel list = null;

                    try {
                        Transaction tx = getSession().beginTransaction();
                        Criteria ct = getSession().createCriteria(Project.class);
                        ct.add( Restrictions.eq( "idUser", userManager.getUser().getId() ) );
                        //ct.add( Restrictions.eq( "isActive", "y" ) );

                        if( getSearchPattern().length() > 0 ) {
                            String pattern = "%" + getSearchPattern() + "%";
                            ct.add( Restrictions.disjunction()
                                  .add( Restrictions.ilike("name", pattern) )
                                  .add( Restrictions.ilike("note", pattern) )
                              );
                        }
                        
                        if( getSortDirection().equalsIgnoreCase( "asc" ) ) {
                            ct.addOrder( Order.asc( getSortByField()) );
                        } else {
                            ct.addOrder( Order.desc( getSortByField()) );
                        }

                        ct.setFirstResult(getPageFirstItem());
                        ct.setMaxResults(getPageSize());
                        list = new ListDataModel(ct.list());
                        tx.commit();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                    return list;
                }
            };
        }

        return paginator;
    }


    /**
     * Returns count of records returned to view.
     * @return int
     */
    public int getRowsPerPage() {
        return rowsPerPage;
    }


    /**
     * Sets count of records returned to view.
     * @param rowsPerPage
     */
    public void setRowsPerPage( int rowsPerPage ) {
        this.rowsPerPage = rowsPerPage;

        resetDataModel();
        resetPagination();
    }


    /**
     * Returns DataModel for dataTable.
     * @return DataModel for dataTable.
     */
    public DataModel getList() {
        if( dataModel == null ) {
            dataModel = getPaginator().createPageDataModel();
        }

        return dataModel;
    }


     /**
     * Returns DataModel for dataTable with recent records.
     * @return DataModel for dataTable.
     */
    public DataModel getListRecent() {
        if( dataModel == null ) {
            dataModel = createPageDataModelRecent();
        }

        return dataModel;
    }


    /**
     * Returns DataModel restricted by current customer id.
     * @return DataModel
     */
    public DataModel createPageDataModelCustomer() {
        ListDataModel list = null;

        try {
            Transaction tx = getSession().beginTransaction();
            Criteria ct = getSession().createCriteria(Project.class);
            ct.add( Restrictions.eq( "idUser", userManager.getUser().getId() ) );
            ct.add( Restrictions.eq( "idCustomer", customerManager.getCustomer().getId() ) );
            //ct.add( Restrictions.eq( "isActive", "y" ) );

            if( getSearchPattern().length() > 0 ) {
                String pattern = "%" + getSearchPattern() + "%";
                ct.add( Restrictions.disjunction()
                      .add( Restrictions.ilike("name", pattern) )
                      .add( Restrictions.ilike("note", pattern) )
                  );
            }

            if( getSortDirection().equalsIgnoreCase( "asc" ) ) {
                ct.addOrder( Order.asc( getSortByField()) );
            } else {
                ct.addOrder( Order.desc( getSortByField()) );
            }

            ct.setFirstResult(getPaginator().getPageFirstItem());
            ct.setMaxResults(getPaginator().getPageSize());
            list = new ListDataModel(ct.list());
            tx.commit();
        } catch (Exception e) {
            e.printStackTrace();
        }

        return list;
    }


    /**
     * Returns DataModel restricted create date and maximum number of recent items.
     * @return DataModel
     */
    public DataModel createPageDataModelRecent() {
        ListDataModel list = null;

        try {
            Transaction tx = getSession().beginTransaction();
            Criteria ct = getSession().createCriteria(Project.class);
            ct.add( Restrictions.eq( "idUser", userManager.getUser().getId() ) );
            ct.addOrder( Order.asc( "create" ) );
            ct.setFirstResult(getPaginator().getPageFirstItem());
            ct.setMaxResults( DSEnum.ITEMS_MAX_RECENT_TIMERS.getValue() );
            list = new ListDataModel(ct.list());
            tx.commit();
        } catch (Exception e) {
            e.printStackTrace();
        }

        return list;
    }


     /**
     * Returns DataModel for dataTable restricted by current customer.
     * @return DataModel for dataTable.
     */
    public DataModel getListCustomer() {
        if( dataModel == null ) {
            dataModel = createPageDataModelCustomer();
        }

        return dataModel;
    }


    /**
     * Next page.
     */
    public void next() {
        getPaginator().nextPage();
        resetDataModel();
    }

    /**
     * Last page.
     */
    public void last() {
        getPaginator().lastPage();
        resetDataModel();
    }


    /**
     * Previous page.
     */
    public void previous() {        
        getPaginator().previousPage();
        resetDataModel();
    }

    /**
     * First page.
     */
    public void first() {
        getPaginator().firstPage();
        resetDataModel();
    }


    /**
     * Increments count of items per page.
     */
    public void more() {
        setRowsPerPage(getRowsPerPage() + DSEnum.ITEMS_PER_PAGE_INCREMENT.getValue());

        resetPagination();
        resetDataModel();
    }


    /**
     * * Decrements count of items per page.
     */
    public void less() {
        setRowsPerPage(getRowsPerPage() - DSEnum.ITEMS_PER_PAGE_INCREMENT.getValue());

        resetPagination();
        resetDataModel();
    }


    /**
     * Retrieves sort params through context and initiates sort.
     */
    public void sort() {
        /**
         * Try to gain params send by <f:param />
         */
        FacesContext context = FacesContext.getCurrentInstance();
        Map requestMap = context.getExternalContext().getRequestParameterMap();
        setSortByField( (String) requestMap.get("by") );
        setSortDirection( (String) requestMap.get("order") );

        resetPagination();
        resetDataModel();
        
    }


      /**
        * Returns customers project count.
        * @return int means projects count.
        */
    public int getCustomerProjectCount() {
            Transaction tx = getSession().beginTransaction();
            String hql = "select count(*) from Project as project where project.idCustomer = '" + customerManager.getCustomer().getId() + "'";
            int count = ( (Long) (getSession().createQuery(hql).iterate().next()) ).intValue();
            tx.commit();
            return count;
        }


     /**
     * Initiates searching.
     */
    public void search() {
        /**
         * Try to gain params send by <f:param />
         */
        //FacesContext context = FacesContext.getCurrentInstance();
        //Map requestMap = context.getExternalContext().getRequestParameterMap();
        //setSortByField( (String) requestMap.get("by") );

        resetPagination();
        resetDataModel();
    }


    /**
     * Cancels searching.
     */
    public void cancelSearch() {
        setSearchPattern( "" );

        resetPagination();
        resetDataModel();
    }


    /**
     * Returns projects count.
     * @return int means projects count.
     */
    public int getProjectsCount() {
        Transaction tx = getSession().beginTransaction();
        String hql = "select count(*) from Project as project where project.idUser = '"
                + userManager.getUser().getId() + "'";
        int num = ( (Long) (getSession().createQuery(hql).iterate().next()) ).intValue();
        tx.commit();
        return num;
    }


    /**
     * Returns count of records in Data Model.
     * @return int
     */
    public int getDataModelRecordsCount() {

        if( getList() != null ) {
            return getList().getRowCount();
        }

        return 0;
    }
    
    
    /**
     * Returns field name in JPA entity according which sorting will be provided.
     * @return String
     */
    public String getSortByField() {
        return sortByField;
    }


    /**
     * Sets field name in JPA entity according which sorting will be provided.
     * @param sortByField
     */
    public void setSortByField(String sortByField) {
        this.sortByField = sortByField;
    }

    
    /**
     * Returns sort direction (ascending: asc; descending desc).
     * @return String
     */
    public String getSortDirection() {
        return sortDirection;
    }


    /**
     * Sets sort direction (ascending: asc; descending desc).
     * @param sortDirection
     */
    public void setSortDirection(String sortDirection) {
        this.sortDirection = sortDirection;
    }


    /**
     * Returns searched string.
     * @return String
     */
    public String getSearchPattern() {
        return searchPattern;
    }


    /**
     * Sets searched string.
     * @param searchPattern
     */
    public void setSearchPattern(String searchPattern) {
        this.searchPattern = searchPattern;
    }


    public Project getProject() {
        return project;
    }

    public void setProject(Project project) {
        this.project = project;

    }

    public void changeRate() {
       if (project.getIdCustomer() == 0) {
               project.setPricePerRateUnit(new Float(0));
               project.setRateType("p");
       }
       else {
           try {
                Customer customer =  getCustomerById( project.getIdCustomer() );
                project.setPricePerRateUnit( customer.getPricePerRateUnit() );
                project.setRateType( customer.getRateType() );
                customer = null;
           } catch (Exception e) {
              e.printStackTrace();
          }
       }
    }


    /**
     * Method returns Customer by his id.
     * @param id Customer ID
     * @return Customer
     */
    public Customer getCustomerById(int id) {
        Transaction tx = getSession().beginTransaction();
        String hql = "from Customer as customer where customer.id = '"+ id +"'";
        Query query = getSession().createQuery( hql );
        query.setMaxResults( 1 );
        Customer customer =  (Customer) query.uniqueResult();
        tx.commit();
        return customer;
    }


    /**
     * Based on actual row, method returns Customer name
     * @return String
     */
    public String getRowCustomerName() {
        // Project tmpProject = (Project) getList().getRowData();
        Project tmpProject = (Project) dataModel.getRowData();
        int idCustomer = tmpProject.getIdCustomer();

        if( idCustomer > 0 ) {
            Customer tmpCustomer = getCustomerById( idCustomer );
            return tmpCustomer.getName();
        }

        return "--";
    }

    /**
     * Based on actual row, method returns Customer name
     * @return String
     */
    public String getCustomerName() {
        // Project tmpProject = (Project) getList().getRowData();
        Project tmpProject = project;
        int idCustomer = tmpProject.getIdCustomer();

        if( idCustomer > 0 ) {
            Customer tmpCustomer = getCustomerById( idCustomer );
            return tmpCustomer.getName();
        }

        return "";
    }


    /**
     * Redirects to customerDetail view.
     * @return String
     */
    public String customerDetail() {
        Project tmpProject = (Project) dataModel.getRowData();
        int idCustomer = tmpProject.getIdCustomer();

        customerManager.setCustomer( getCustomerById( idCustomer) );

        tmpProject = null;

        return "/user/customer/detail" + NAV_REDIRECT;
    }


    /**
     * Returns status of null flag.
     * @return
     */
    public boolean isNullFlag() {
        return nullFlag;
    }


    /**
     * Sets null flag.
     * @param nullFlag
     */
    public void setNullFlag(boolean nullFlag) {
        this.nullFlag = nullFlag;
    }


    /**
     * Returns id's projects of user.
     */
    public ArrayList<Integer> getProjectIds() {
        Transaction tx = getSession().beginTransaction();
        String hql = "select id from Project as project where project.idUser = '" + userManager.getUser().getId() + "'";
        ArrayList<Integer> result = new ArrayList<Integer>();
        Iterator it = getSession().createQuery( hql ).iterate();
        
        while( it.hasNext() ) {
            result.add( (Integer) it.next() );
        }
        
        tx.commit();

        return result;
    }


    public Project getProjectById( int id ) {
        Transaction tx = getSession().beginTransaction();
        String hql = "from Project as project where project.id = '"+ id +"'";
        Query query = getSession().createQuery( hql );
        query.setMaxResults( 1 );
        project =  (Project) query.uniqueResult();
        tx.commit();
        return project;
    }

}
